#include "informationadaptability.h"

InformationAdaptability::InformationAdaptability() {
	this->name = "INFORMATION_ADAPTABILITY";
	this->estimatedTaskTimes = NULL;
}

InformationAdaptability::~InformationAdaptability() {
	releaseMatrix();
}

long double InformationAdaptability::schedule(Grid *grid, Job *job) {
		printf("Scheduling with InformationAdaptability!");
	//Implement here. Maybe it will be necessary to create private  sub-methods
	return 0;	
}

void InformationAdaptability::initMatrix() {
	if (this->estimatedTaskTimes)  {
		free(this->estimatedTaskTimes);
		this->estimatedTaskTimes = NULL;
	}

	this->estimatedTaskTimes = (TaskTimesMatrix *) calloc(  job->getNumberOfTasks(), sizeof(TaskTimesMatrix));
	
	if ((this->estimatedTaskTimes==NULL)) {
		throw std::runtime_error("Not enough memory for 'estimatedTaskTimes'!\n");
	}
	
	for (int i = 0; i< job->getNumberOfTasks(); i++) {
		
		this->estimatedTaskTimes[i].completionTime = LONG_LONG_MAX;
		this->estimatedTaskTimes[i].siteIndexCT = -1;
		this->estimatedTaskTimes[i].procIndexCT = -1;
		this->estimatedTaskTimes[i].executionTime = LONG_LONG_MAX;
		this->estimatedTaskTimes[i].siteIndexET = -1;
		this->estimatedTaskTimes[i].procIndexET = -1;			
		this->estimatedTaskTimes[i].transferTime = LONG_LONG_MAX;
		this->estimatedTaskTimes[i].siteIndexTT = -1;

	}
}

void InformationAdaptability::updateMatrix() {
	
	int proc = -1;
	
	for (int t = 0; t < job->getNumberOfTasks(); t++) {
		
		Task *task = job->getTaskByIndex(t);
		
		for (int s = 0; s < grid->getNumberOfSites(); s++) {
			
			Site *site = grid->getSiteByIndex(s);
			
			if (site->getNumberOfProcessors()>0) {

				/*Sites with all information available. Get the best site and the respective processor for each task */
				if (site->hasAllAvailableInformation()) {
					long double currentCT = getSiteCompletionTime(t, s, &proc);				
					if ( currentCT < this->estimatedTaskTimes[t].completionTime) {
						this->estimatedTaskTimes[t].completionTime = currentCT;
						this->estimatedTaskTimes[t].siteIndexCT = s;
						this->estimatedTaskTimes[t].procIndexCT = proc;
					}
					
				} 
				/*Sites with processor information available. Get the best site and the respective processor for each task*/			
				if (site->hasProcessorAvailableInformation()) {

					int bestProc = 0;
					long double bestExec = getEstimatedExecutionTime(t,bestProc,s);
					for (int p = 1; p < site->getNumberOfProcessors(); p++) {
						long double currExec = getEstimatedExecutionTime(t,p,s);
						if  (currExec < bestExec) {
							bestExec = currExec;
							bestProc = p;
						}
					}
					
					if (bestExec < this->estimatedTaskTimes[t].executionTime) {
						this->estimatedTaskTimes[t].executionTime = bestExec;
						this->estimatedTaskTimes[t].siteIndexET = s;
						this->estimatedTaskTimes[t].procIndexET = bestProc;
					}
					
				}
				/*Sites with network information available. Get the best site (fast link) for each task*/
				if (site->hasNetworkAvailableInformation()) {
					
					long double currentTT = getEstimatedTransferTime(task, s, Task::IN) + getEstimatedTransferTime(task, s, Task::OUT);
					if (currentTT < this->estimatedTaskTimes[t].transferTime) {
						this->estimatedTaskTimes[t].transferTime = currentTT;
						this->estimatedTaskTimes[t].siteIndexTT = s;
					}
				}
				
			}

			
		}
	}
	
}

void InformationAdaptability::releaseMatrix() {
	if (this->estimatedTaskTimes)  {
		free(this->estimatedTaskTimes);
		this->estimatedTaskTimes = NULL;
	}

}

void InformationAdaptability::doSimGridSimulation(SG_Task task_to_watch) {
	
//	SG_Task *tasks_finished = SG_simulate(-1, task_to_watch, SG_VERBOSE);
//	
//	//Executed only during the replication phase
//	this->killAllReplicasOf(tasks_finished);
//
//	/*Get task state distribution*/
// 	SG_getStateDistribution(&numberOfTasksNotScheduled, &numberOfTasksScheduled, &numberOfTasksReady, 
// 	&numberOfTasksRunning, &numberOfTasksFailed, &numberOfTasksFinished);
//	 	
// 	fprintf(stderr,"\n------------Distribution of tasks-------------");
// 	fprintf(stderr,"\nNot Scheduled=%d", this->numberOfTasksNotScheduled);
// 	fprintf(stderr,"\nScheduled=%d", this->numberOfTasksScheduled);	
// 	fprintf(stderr,"\nReady=%d", this->numberOfTasksReady);
// 	fprintf(stderr,"\nRunning=%d", this->numberOfTasksRunning);
// 	fprintf(stderr,"\nFailed=%d", this->numberOfTasksFailed);
// 	fprintf(stderr,"\nFinished=%d", this->numberOfTasksFinished);
// 	fprintf(stderr,"\n----------------------------------------------\n");
 	
}
